home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume13 / e < prev    next >
Encoding:
Internet Message Format  |  1988-01-31  |  49.2 KB

  1. Subject:  v13i060:  Friendly front-end to vi
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Crocodile Dundee <tcjones%watdragon.waterloo.edu@RELAY.CS.NET>
  7. Posting-number: Volume 13, Issue 60
  8. Archive-name: e
  9.  
  10. Basically e is a command line preprocessor to get you into vi with ease.
  11. I find it useful - even given a shell with history and command completion. 
  12. It keeps a short history of the files that have been e'ed most recently in 
  13. each directory. Thus it is nice to be able to do
  14.  
  15. cd somewhere
  16. e
  17.  
  18. and get straight into the file you were last working on in the directory 
  19. "somewhere". It also allows fast cross-directory editing and does obvious 
  20. spelling corrections.
  21.  
  22.  
  23.  
  24. #!/bin/sh
  25. # This is a shell archive.  Remove anything before this line,
  26. # then unpack it by saving it in a file and typing "sh file".
  27. #
  28. # Contents:  README e.cat e.1 Makefile MANIFEST MODS e.h e.c
  29.  
  30. echo x - README
  31. sed 's/^@//' > "README" <<'@//E*O*F README//'
  32. README - version 1.2
  33. --------------------
  34.  
  35. BLURB.
  36. ======
  37.  
  38. Basically e is a command line preprocessor to get you into vi with ease.
  39. I find it useful - even given a shell with history. It keeps a short history
  40. of the files that have been e'ed most recently in each directory. Thus it is 
  41. nice to be able to do
  42.  
  43. cd somewhere
  44. e
  45.  
  46. and get straight into the file you were last working on in the directory 
  47. "somewhere". It also allows fast cross-directory editing and does obvious 
  48. spelling corrections.
  49.  
  50. DOCUMENTATION
  51. =============
  52.  
  53. Documentation and examples can be found in the man page in e.1. 
  54. This is in troff -man format. e.cat contains the troffed output of e.1.
  55.  
  56. MAKING
  57. ======
  58.  
  59. Before making you should change the #define VI line in e.h depending on the
  60. location of your vi. Also change the DESTDIR line in Makefile.
  61.  
  62. This will run just fine on BSD4.[23]. Also on Ultrix-32.
  63. It appears to work on a SUN as well, just #define NFS in e.h
  64. I am told it will also run on sysV, just #define sysV in e.h
  65.  
  66.  
  67. THANKS
  68. ======
  69.  
  70. To Phil Oldiges and Simon Brown for the man page and the SysV portability 
  71. changes.
  72.  
  73. This appears free of major bugs (famous last words) - I'd like to hear
  74. about anything that goes wrong and/or fixes/enhancements. Comments on 
  75. what is wrong and how it could be better/faster are also welcome...
  76.  
  77.  
  78.  
  79. -------------------------------------------------------------------------------
  80. Terry Jones, Department Of Computer Science, University Of Waterloo
  81.              Waterloo Ontario Canada N2L 3G1
  82.  
  83. {ihnp4,allegra,decvax,utzoo,utcsri,clyde}!watmath!watdragon!tcjones
  84. tcjones@dragon.waterloo.{cdn,edu} tcjones@WATER.bitnet
  85. tcjones%watdragon@waterloo.csnet [from oz, tcjones@dragon.waterloo.cdn@munnari]
  86. -------------------------------------------------------------------------------
  87.  
  88. @//E*O*F README//
  89. chmod u=rw,g=rx,o=rx README
  90.  
  91. echo x - e.1
  92. sed 's/^@//' > "e.1" <<'@//E*O*F e.1//'
  93. @.TH E P "December 15, 1987"
  94. @.SH NAME
  95. e \- command line preprocessor for the vi(1) editor. 
  96. @.SH SYNOPSIS
  97. @.B e
  98. [
  99. @.B option
  100. ] [
  101. @.B file
  102. ] ...
  103. @.SH DESCRIPTION
  104. @.I e 
  105. is an interface to 
  106. @.IR vi (1)
  107. that maintains a history of the most recently \fIe\fR'ed
  108. files for each directory. Its invocation syntax is (almost) a superset of 
  109. that of \fIvi\fR.
  110. @.PP
  111. A file called .e is kept in each directory. This contains the history and is 
  112. kept to a small length (<10 lines). Spelling corrections are suggested for 
  113. simple mistakes (wrong character, omitted character, interchanged 
  114. characters, extra character). 
  115. The history file is rearranged with each use to place the last \fIe\fR'ed 
  116. file at the end of the list. Duplicate entries are removed.
  117. If set, the environment variable VIPATH is read and the directories it contains
  118. are searched for an existing file with the same name before a new file is
  119. created. Thus one can vi across directories with ease.
  120. @.SH INVOCATION
  121. A list of the command line variations is given below.
  122. [x] indicates that "x" is optional. "cmd" means a 
  123. @.I vi 
  124. command.
  125. @.TP 18
  126. @.B e           
  127. Invokes \fIvi\fR on the last file that was \fIe\fR'ed in this directory.
  128. @.TP 18
  129. @.B e \-
  130. Prints the history for this directory and allows selection
  131. of a previous file - or a new one.
  132. @.TP 18
  133. @.B e .
  134. Prints the history for this directory without asking for input.
  135. @.TP 18
  136. @.B e \-t tag
  137. Invokes \fIvi\fR \-t tag
  138. @.TP 18
  139. @.B e \-r
  140. Invokes \fIvi\fR \-r
  141. @.TP 18
  142. @.B e \-r file
  143. Invokes \fIvi\fR \-r file
  144. @.TP 18
  145. @.B e \-pat
  146. \fIvi\fR's the last file that was \fIe\fR'ed with the string "pat" on
  147. the command line.
  148. @.TP 18
  149. @.B e +[cmd]
  150. \fIvi\fR's the last file that was \fIe\fR'ed in this directory but executes
  151. command "cmd" on the way into.
  152. @.I vi
  153. @.TP 18
  154. @.B e [+[cmd]] file  
  155. \fIvi\fR's the file and adds it to the history list. Minor spelling
  156. corrections are suggested if "file" does not exist but is close 
  157. (in spelling) to some file that does.
  158. @.TP 18
  159. @.B e [+[cmd]] files 
  160. \fIvi\fR's the files and adds them as a single entry to the history.
  161. @.SH EXAMPLE
  162. @.PP
  163. With a ".e" file containing:
  164. @.in 1.0i
  165. @.B fred.c
  166. @.br
  167. @.B jane
  168. @.br
  169. @.B alison
  170. @.br
  171. @.B +/main pete.c
  172. @.br
  173. @.B bigmac
  174. @.br
  175. @.B fries juice
  176. @.br
  177. @.PP 2
  178. The command "\fBe .\fR" will give:
  179. @.in 1.0i
  180. @.B [5]: fred.c
  181. @.br
  182. @.B [4]: jane
  183. @.br
  184. @.B [3]: alison
  185. @.br
  186. @.B [2]: +/main pete.c
  187. @.br
  188. @.B [1]: bigmac
  189. @.br
  190. @.B [0]: fries juice
  191. @.br
  192. @.TP 12
  193. @.B e
  194. will get you fries and then juice.
  195. @.TP 12
  196. @.B e \-
  197. will present the same as shown for "\fBe .\fR" above
  198. but will ask for a number or another filename.
  199. Carriage return is equivalent to zero (i.e. the last filename),
  200. backspace or interrupt will quit.
  201. @.TP 12
  202. @.B e \-ali
  203. will get you alison.
  204. @.TP 12
  205. @.B e +/ketchup
  206. will get you fries and juice, searching for ketchup in fries.
  207. @.TP 12
  208. @.B e bigamc
  209. will ask if you want to correct to bigmac.
  210. "n" will not do the correction,
  211. "Q" or "q" will quit, anything else will go ahead.
  212. If there is more than one possible correction you will be prompted for each in
  213. turn. A response of "N" will means that you really want what you typed, no
  214. further corrections will be offered.
  215. @.SH "ENVIRONMENT VARIABLES"
  216. With an environment variable VIPATH set to "$HOME /usr/include/sys",
  217. @.B "e inode.h" 
  218. will prompt you with /usr/include/sys/inode.h 
  219. if no file called inode.h exists in the current directory or your home
  220. directory. You can say "y", "n" or "q" to accept, reject or quit. 
  221. If you accept then the file is put into the history. Spelling corrections 
  222. are not suggested across directories.
  223. There is (of course) no need to put "." in your VIPATH. Doing so will just slow
  224. things down and cannot possibly be of help.
  225. This should be clear, "." is always searched first for the given filename.
  226. Putting it into your VIPATH will have it searched twice. The directory names
  227. in VIPATH may be separated by white space (including newlines) and colons.
  228. @.SH NOTES
  229. When using 
  230. @.B "e -" 
  231. the terminal is put into cbreak mode. If the first
  232. character typed is a digit (in the acceptable range of history items)
  233. then you will get that history item without further ado. Thus if you
  234. have a file called 4play and you try and 
  235. @.B "e" 
  236. it from within a 
  237. @.B "e -"
  238. then you'll probably end up in the wrong place.
  239. This is to say you'll get the file that was the 4th last in the history.
  240. @.PP
  241. The history length must be less than or equal to 9 (the code sets
  242. it to 8 at present). 
  243. The problem with having more is that with 
  244. @.B "e -" 
  245. you go into cbreak
  246. and the first digit entered (say \fIn\fP) is taken to mean 
  247. "I want the \fIn\fRth last file". This saves the need for hitting return. 
  248. With the history being kept as a most recently used list, 8 file names 
  249. should be enough.
  250. @.SH BUGS
  251. The first character on a select line cannot be backspaced over.
  252. @.SH "THINGS TO DO"
  253. Make .exrc files be inherited when \fIe\fR'ing across directories.
  254. @.br
  255. Do spelling corrections across directories.
  256. @.br
  257. Plenty of fancy things.
  258. Keep a single .e file in $HOME and then it is possible to use history features
  259. in unwriteable directories etc. (suggested by ian! allen)
  260. @.br
  261. Version 2 will be a complete rewrite and will clean up the above and hopefully
  262. add cross directory spelling correction and other useful things.
  263. @.SH "SEE ALSO"
  264. ex (1), edit (1).
  265. @.SH AUTHOR
  266. Terry Jones
  267. @.br
  268. Department of Computer Science
  269. @.br
  270. University of Waterloo
  271.  
  272. @.\"
  273. @.\" Man page for e. Version 1.2
  274. @.\"
  275. @.\"    ------------------------------------------------------------------------
  276. @.\"    Terry Jones, Department Of Computer Science, University Of Waterloo
  277. @.\"         Waterloo Ontario Canada N2L 3G1
  278. @.\"
  279. @.\"    {ihnp4,allegra,decvax,utzoo,utcsri,clyde}!watmath!watdragon!tcjones
  280. @.\"    tcjones@dragon.waterloo.{cdn,edu} tcjones@WATER.bitnet
  281. @.\"    tcjones%watdragon@waterloo.csnet 
  282. @.\"    -------------------------------------------------------------------------
  283. @.\"
  284. @//E*O*F e.1//
  285. chmod u=rw,g=rx,o=rx e.1
  286.  
  287. echo x - Makefile
  288. sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//'
  289. #
  290. # Makefile for e version 1.2
  291. #
  292.  
  293. CC = /bin/cc
  294. CFLAGS = -O 
  295. DESTDIR = /u/tcjones/bin
  296. DEST = $(DESTDIR)/e
  297. OBJ = e.o
  298. HEADERS = e.h
  299.  
  300. $(DEST) : $(HEADERS) $(OBJ)
  301.     $(CC) $(CFLAGS) -o $(DEST) $(OBJ)
  302.     strip $(DEST)
  303. @//E*O*F Makefile//
  304. chmod u=rw,g=rx,o=rx Makefile
  305.  
  306. echo x - MANIFEST
  307. sed 's/^@//' > "MANIFEST" <<'@//E*O*F MANIFEST//'
  308. The shar for version 1.2 should come with eight files
  309.  
  310.     Makefile
  311.     MANIFEST
  312.     README
  313.     e.1
  314.     e.c
  315.     e.cat
  316.     e.h
  317.     MODS
  318. @//E*O*F MANIFEST//
  319. chmod u=rw,g=rx,o=rx MANIFEST
  320.  
  321. echo x - MODS
  322. sed 's/^@//' > "MODS" <<'@//E*O*F MODS//'
  323. e Modification History.
  324. =======================
  325.  
  326.  
  327.  
  328.  
  329. Version 1.1 changes          October 1987.
  330. ==========================================
  331.  
  332. - Added "VIPATH" environment variable.
  333. - Fixed bug with argument lists that exceeded ARG_CHARS chars in multiple().
  334. - Added #ifdef statements to enable compilation on a SUN.
  335. - Put #defines etc into e.h as they were getting in the way.
  336.  
  337.  
  338.  
  339. Version 1.2 changes          December 1987.
  340. ===========================================
  341.  
  342. - Made the numbering on "e -" the same as for "e .".
  343. - System V port (thanks to Simon Brown).
  344. - Added man page (thanks to Phil Oldiges).
  345. - Fixed handling of the VIPATH variable so that it doesn't screw up
  346.     when there is more than one delimiter. Added new delimiters.
  347. - Cleaned up terminal handling to use ioctl().
  348. - Cleaned up error handling.
  349. - Fixed (for sure this time) the bug with long argument lists.
  350. - Made returns consistent in check().
  351. - Flagged suspected unsatisfied symbolic link in check().
  352. - Added #define called E_PATH which contains the name of the environment
  353.     variable containing the PATH for cross directory stuff. (set to "VIPATH").
  354. - Added E_MODE #define for the protection mode for .e (set to 0644).
  355. - Fixed spelling correction to offer all likely targets in a directory, not
  356.     just the first encountered. Added 'N' option to say "I meant what I said,
  357.     don't give me more possible corrections."
  358. - Comments!
  359.  
  360.  
  361. To be added?
  362. ============
  363.  
  364. - Allow backspacing on a select line.
  365. - Make .exrc files be inherited when e'ing across directories.
  366. - Spelling corrections across directories.
  367. - Plenty of fancy things.
  368. - Keep a single .e file in $HOME and then it's easy to use the history in
  369.    unwriteable directories etc. do this nicely with hashing/binary search etc.
  370.    (suggested by ian! allen)
  371.  
  372.  
  373.  
  374.  
  375. -------------------------------------------------------------------------------
  376. Terry Jones, Department Of Computer Science, University Of Waterloo
  377.              Waterloo Ontario Canada N2L 3G1
  378.  
  379. {ihnp4,allegra,decvax,utzoo,utcsri,clyde}!watmath!watdragon!tcjones
  380. tcjones@dragon.waterloo.{cdn,edu} tcjones@WATER.bitnet
  381. tcjones%watdragon@waterloo.csnet [from oz, tcjones@dragon.waterloo.cdn@munnari]
  382. -------------------------------------------------------------------------------
  383.  
  384. @//E*O*F MODS//
  385. chmod u=rw,g=rx,o=rx MODS
  386.  
  387. echo x - e.h
  388. sed 's/^@//' > "e.h" <<'@//E*O*F e.h//'
  389. /*
  390.  * e.h for e version 1.2
  391.  * =====================
  392.  *
  393.  *------------------------------------------------------------------------------
  394.  * terry jones, department of computer science, university of waterloo
  395.  *                waterloo ontario canada N2L 3G1
  396.  
  397.  * {ihnp4,allegra,decvax,utzoo,utcsri,clyde}!watmath!watdragon!tcjones
  398.  * tcjones@dragon.waterloo.{cdn,edu} tcjones@WATER.bitnet
  399.  * tcjones%watdragon@waterloo.csnet 
  400.  *------------------------------------------------------------------------------
  401.  */
  402.  
  403.  
  404.  
  405.  
  406. /*
  407.  * #includes common to all versions of uni*x
  408.  *
  409.  */
  410.  
  411.  
  412. #include <stdio.h>
  413. #include <sys/types.h>
  414. #include <sys/stat.h>
  415. #include <sys/file.h>
  416. #include <signal.h>
  417. #include <ctype.h>
  418. #include <sys/param.h>
  419.  
  420.  
  421.  
  422. /*
  423.  * un*x version dependent #includes 
  424.  *
  425.  */
  426.  
  427.  
  428. #ifdef sysV
  429. #  ifdef GEC
  430. #    include <local/ndir.h>
  431. #  else
  432. #    include <ndir.h>
  433. #  endif
  434. #  include <termio.h>
  435. #  include <string.h>
  436. #  include <fcntl.h>
  437. #else
  438. #  include <sys/dir.h>
  439. #  ifdef NFS
  440. #    include <time.h>
  441. #    include <sys/vnode.h>
  442. #    include <ufs/inode.h>
  443. #    include <sgtty.h>
  444. #  else
  445. #    include <sys/inode.h>
  446. #    include <sys/ioctl.h>
  447. #  endif
  448. #  include <strings.h>
  449. #endif sysV
  450.  
  451.  
  452.  
  453.  
  454. /*
  455.  * un*x dependent #defines
  456.  *
  457.  */
  458.  
  459.  
  460. #ifdef sysV
  461. #  define VI "/usr/bin/vi"
  462. #  define index strchr
  463. #  define rindex strrchr
  464. #else
  465. #  define VI "/usr/ucb/vi"
  466. #endif sysV
  467.  
  468.  
  469. /*
  470.  * Other checks...
  471.  *
  472.  */
  473.  
  474. #ifndef L_SET
  475. # define L_SET 0
  476. #endif
  477.  
  478. #ifndef IREAD
  479. # define IREAD    0400
  480. # define IWRITE    0200
  481. #endif
  482.  
  483.  
  484.  
  485. /*
  486.  * The #defines for things that aren't portability concerns.
  487.  *
  488.  */
  489.  
  490.  
  491.  
  492. #define HIST ".e"
  493. #define HIST_LINES 8
  494. #define HIST_CHARS 1024
  495. #define E_PATH "VIPATH"
  496. #define E_MODE 0644
  497. #define ARG_CHARS 1024
  498. #define MAX_ARGS 100
  499. #define MAX_PATH 1024
  500. #define BELL '\007'
  501. #define O_READ 00004
  502. #define G_READ 0004
  503.  
  504.  
  505.  
  506. /*
  507.  * #defines for the terminal() function.
  508.  *
  509.  */
  510.  
  511. #define TERM_RECORD 0
  512. #define TERM_SET 1
  513. #define TERM_RESET 2
  514.  
  515.  
  516.  
  517. /*
  518.  * STRUCT_ASST should be defined if your UN*X is capable of doing 
  519.  * structural assignment. This is (supposedly) true for System III, System V,
  520.  * UNIX/32V, BSD4.[23], v8 and v9. It's also in ANSI C, but *not* in 
  521.  * "The C programming language" by Kernhigan & Ritchie.
  522.  *
  523.  * This #define is only used in the function terminal(). Leave STRUCT_ASST
  524.  * defined and if it doesn't break you're ok. If it does, undefine it and
  525.  * you'll definitely be ok (but things will run slower - even though you
  526.  * wont notice that anyway.) Words words words. Ho hum.
  527.  *
  528.  */
  529.  
  530. #define STRUCT_ASST
  531.  
  532.  
  533. /*
  534.  * A slightly dangerous one. NEVER call this as (for example) "is_delim(cp++)"
  535.  * Or the incrementing will be done 4 times (not one).
  536.  */
  537.  
  538.  
  539. #define is_delim(c) ((*c)==' '||(*c)==':'||(*c)=='\t'||(*c)=='\n')
  540. @//E*O*F e.h//
  541. chmod u=rw,g=rx,o=rx e.h
  542.  
  543. echo x - e.c
  544. sed 's/^@//' > "e.c" <<'@//E*O*F e.c//'
  545.  
  546. /******************************************************************************
  547.  #                                                                            #
  548.  #                                                                            #
  549.  # e - Command line preprocessor for vi.  Version 1.2 - November 1987.        #
  550.  # ======================================================================     #
  551.  #                                                                            #
  552.  #                                                                            #
  553.  # Terry Jones                                                                #
  554.  # Department of Computer Science                                             #
  555.  # University of Waterloo                                                     #
  556.  # Waterloo, Ontario, Canada. N2L 3G1                                         #
  557.  #                                                                            #
  558.  # {ihnp4,allegra,decvax,utzoo,utcsri,clyde}!watmath!watdragon!tcjones        #
  559.  # tcjones@dragon.waterloo.{cdn,edu} tcjones@WATER.bitnet                     #
  560.  # tcjones%watdragon@waterloo.csnet                                           #
  561.  #                                                                            #
  562.  #*****************************************************************************/
  563.  
  564.  
  565. #include "e.h"
  566.  
  567.  
  568. /*
  569.  * Globals. All in the name of ease and perhaps speed.
  570.  *
  571.  */
  572.  
  573. char history[HIST_CHARS];
  574. char arg[ARG_CHARS];
  575. char *hist[HIST_LINES];
  576. char temp[HIST_CHARS];
  577. char *tmp_file=".e_tmpXXXXXX";
  578. char erase;
  579. char *myname;
  580.  
  581.  
  582. clean_up()
  583. {
  584.     /* 
  585.      * This is where we come when an interrupt is received.
  586.      * Just get out after making sure things are tidy.
  587.      *
  588.      */
  589.  
  590.     e_error("\n%s","Interrupt.");
  591. }
  592.  
  593.  
  594.  
  595. main(c,v)
  596. int c;
  597. char **v;
  598. {
  599.     myname=v[0];
  600.     terminal(TERM_RECORD);
  601.  
  602.     /* handle SIGINT */
  603.     if (signal(SIGINT,SIG_IGN)!=SIG_IGN){
  604.         signal(SIGINT,clean_up);
  605.     }
  606.  
  607.  
  608.  
  609.     /*
  610.      * Process the command line. This gets a little messy as there are so
  611.      * many ways e can be invoked. They are listed below and there is an
  612.      * example provided in each case statement to illustrate the 
  613.      * particular case we are trying to handle.
  614.      *
  615.      * The idea in most cases is to get the arguments that will be passed
  616.      * to vi into a character array (arg), and pass it to do_vi(). do_vi()
  617.      * splits up the arguments and execs vi. Occasionally it is simpler and
  618.      * do_vi() can be called as do_vi("fred").
  619.      *
  620.      *
  621.      * Command Line Options.
  622.      * =====================
  623.      *
  624.      * No arguments.
  625.      *
  626.      * (1) "e"
  627.      *
  628.      * One argument.
  629.      *
  630.      * (3) "e -"
  631.      * (2) "e -n"                   Where n is some valid history item.
  632.      * (4) "e -t"                   If they do this then they are in error!
  633.      * (5) "e -r"
  634.      * (6) "e -pat"
  635.      * (7) "e +100"
  636.      * (8) "e ."
  637.      * (9) "e fred"
  638.      *
  639.      * Multiple arguments.
  640.      *
  641.      * (10) "e fred harry joe"      Also handles "e -t tag", "e -r file" etc.
  642.      *
  643.      */
  644.  
  645.  
  646.     switch (c){
  647.         case 1: {
  648.  
  649.             /* 
  650.              * Command line option (1).
  651.              * Example: "e"
  652.              *
  653.              * Just go and vi the last file that was e'ed.
  654.              *
  655.              */
  656.  
  657.             last_file();
  658.             do_vi(arg);
  659.             break;
  660.         }
  661.         
  662.         case 2:{
  663.             switch ((*++v)[0]){
  664.  
  665.                 case '-':{
  666.  
  667.                     if ((c=(*v)[1])=='\0'){
  668.  
  669.                         /* 
  670.                          * Command line option (2).
  671.                          * Example: "e -"
  672.                          *
  673.                          * This is a select from history, ask what they want.
  674.                          *
  675.                          */
  676.  
  677.                         ask_hist();
  678.                         do_vi(arg);
  679.                     }
  680.                     else if (isdigit(c)){
  681.  
  682.                         /* 
  683.                          * Command line option (3).
  684.                          * Example: "e -3"
  685.                          *
  686.                          * Get the nth last file from the history and vi it.
  687.                          *
  688.                          */
  689.  
  690.                         nth_hist(c-'0');
  691.                         do_vi(arg);
  692.                     }
  693.                     else if (c=='t'&&(*v)[2]=='\0'){
  694.  
  695.                         /* 
  696.                          * Command line option (4).
  697.                          * Example: "e -t"
  698.                          *
  699.                          * This is an empty tag - ignore it.
  700.                          * They have made a mistake, but let vi tell them.
  701.                          *
  702.                          */ 
  703.  
  704.                         do_vi(*v);
  705.                     }
  706.                     else if (c=='r'&&(*v)[2]=='\0'){
  707.  
  708.                         /* 
  709.                          * Command line option (5).
  710.                          * Example: "e -r"
  711.                          *
  712.                          * A recover, just pass it to vi and don't interfere.
  713.                          *
  714.                          */
  715.  
  716.                         do_vi(*v);
  717.                     }
  718.                     else{
  719.  
  720.                         /* 
  721.                          * Command line option (6).
  722.                          * Example: "e -pat"
  723.                          *
  724.                          * This is a pattern - try to match it.
  725.                          *
  726.                          */
  727.  
  728.                         find_match(++*v);
  729.                         do_vi(arg);
  730.                     }
  731.                     break;
  732.                 }
  733.  
  734.                 case '+':{
  735.  
  736.                     /* 
  737.                      * Command line option (7).
  738.                      * Example: "e +100"
  739.                      *
  740.                      * A command, put it before the last file name.
  741.                      *
  742.                      */
  743.  
  744.                     insert_command(*v);
  745.                     do_vi(arg);
  746.                     break;
  747.                 }
  748.  
  749.                 case '.':{
  750.  
  751.                     /* 
  752.                      * Command line option (8).
  753.                      * Example: "e ."
  754.                      * Example: "e .login"  (falls through to option (9)).
  755.                      *
  756.                      * Just give a history list if there is only a dot.
  757.                      * Otherwise fall through as it must be a filename.
  758.                      *
  759.                      */
  760.  
  761.                     if ((*v)[1]=='\0'){
  762.                         register ct;
  763.                         register i;
  764.  
  765.                         read_hist();
  766.                         ct=split_hist();
  767.  
  768.                         for (i=0;i<ct;i++){
  769.                             fprintf(stderr,"\t[%d]: %s\n",ct-i-1,hist[ct-i-1]);
  770.                         }
  771.                         exit(0);
  772.                     }
  773.                     /* 
  774.                      * WARNING!
  775.                      * The switch falls through in the case where there is a
  776.                      * filename that starts with a period.
  777.                      *
  778.                      */
  779.                 }
  780.  
  781.                 default :{
  782.  
  783.                     /* 
  784.                      * Command line option (9).
  785.                      * Example: "e fred"
  786.                      * Example: "e .login"  (fell through from option (8)).
  787.                      *
  788.                      * Looks like it's just a plain old file name. vi it!
  789.                      *
  790.                      */
  791.  
  792.                     normal(*v);
  793.                     do_vi(arg);
  794.                     break;
  795.                 }
  796.             }
  797.         }
  798.  
  799.         default:{
  800.  
  801.             /* 
  802.              * Command line option (10).
  803.              * Example: "e fred harry joe"
  804.              *
  805.              * A bunch of arguments, fix the history & vi them all as normal.
  806.              *
  807.              */
  808.  
  809.             multiple(c,v,ARG_CHARS);
  810.             do_vi(arg);
  811.             break;
  812.         }
  813.     }
  814. }
  815.  
  816.  
  817.  
  818. do_vi(thing)
  819. char *thing;
  820. {
  821.     /* 
  822.      * Split the arguments (if any) in 'thing' up and exec vi on them.
  823.      * The arguments must be space separated.
  824.      *
  825.      */
  826.     char *args[MAX_ARGS];
  827.     char *this,*next;
  828.     register i;
  829.  
  830.     args[0]="vi";
  831.     args[1]=thing;
  832.  
  833.     i=1;
  834.     while (*thing!='\0'&&(thing=index(thing,' '))!=NULL){
  835.         *thing++='\0';
  836.         if (*thing!='\0'){
  837.             args[++i]=thing;
  838.         }
  839.     }
  840.     args[++i]=NULL;
  841.         
  842.     if (execvp(VI,args)==-1){
  843.         e_error("%s %s","Could not execvp",VI);
  844.     }
  845. }
  846.  
  847.  
  848.  
  849. read_hist()
  850. {
  851.     /*
  852.        Read the history file and break it up into lines in the global variable
  853.        'history'. Do the appropriate checks to see that it exists.
  854.     */
  855.  
  856.     register vh;
  857.     register bytes;
  858.     register offset;
  859.     struct stat buf;
  860.  
  861.     /* 
  862.      * If there is no history file then say so and get out of here - they 
  863.      * had no business asking for access to the history.
  864.      *
  865.      */
  866.  
  867.     if ((vh=open(HIST,O_RDONLY))==-1){
  868.         e_error("%s %s","Could not open",HIST);
  869.     }
  870.  
  871.     /* Stat it */
  872.     if (fstat(vh,&buf)==-1){
  873.         e_error("%s %s","Could not stat",HIST);
  874.     }
  875.  
  876.     /* 
  877.      * Set 'offset' so that we can read the last portion of the history
  878.      * file only. If there are less than HIST_CHARS characters in the
  879.      * file then we will start reading at 0, otherwise at HIST_CHARS
  880.      * characters before the end of the file.
  881.      *
  882.      */
  883.  
  884.     offset=(int)buf.st_size-HIST_CHARS<0 ? 0 : buf.st_size-HIST_CHARS;
  885.  
  886.     /* Move (if it's non zero) to that place in the file. */
  887.  
  888.     if (offset&&lseek(vh,(long)offset,L_SET)==-1){
  889.         e_error("%s %s","Could not lseek in",HIST);
  890.     }
  891.  
  892.     /* And read. */
  893.     if ((bytes=read(vh,history,HIST_CHARS))==-1){
  894.         e_error("%s %s","read: Could not read",HIST);
  895.     }
  896.  
  897.     /* If we didn't come up with ANYTHING we may as well leave. */
  898.     if (!bytes){
  899.         e_error("%s %s %s","Empty",HIST,"file.");
  900.     }
  901.  
  902.     /* Zap the newline (which *should* be there) for now. */
  903.     if (history[--bytes]=='\n'){
  904.         history[bytes]='\0';    
  905.     }
  906.  
  907.     /* And get out of here. */
  908.     return(bytes);
  909. }
  910.  
  911.  
  912.  
  913. last_file()
  914. {
  915.     /*
  916.      * Get the last name from the 'history' array and put it into 'arg'.
  917.      *
  918.      */
  919.  
  920.     read_hist();
  921.     if (index(history,'\n')==NULL){
  922.         if (*history=='\0'){
  923.             e_error("%s %s",HIST,"has a line with no newline character.");
  924.         }
  925.         else{
  926.             sprintf(arg,"%s",history);
  927.         }
  928.     }
  929.     else{
  930.         sprintf(arg,"%s",rindex(history,'\n')+1);
  931.     }
  932. }
  933.  
  934.  
  935.  
  936. split_hist()
  937. {
  938.     /*
  939.      * Set the array of pointers in 'hist' to point to the succesive names
  940.      * in the 'history' array. These are delimited (presumably) by newlines
  941.      * and so they're easy to catch.
  942.  
  943.      * What in fact is done is that the history array is copied and we set
  944.      * a pointer (in "hist") to the start of each new item and set the 
  945.      * newline characters to be NULLs. This way we don't mess up the history 
  946.      * array as we will want it intact later on (maybe).
  947.      *
  948.      */
  949.  
  950.     char *tmp;
  951.     register count;
  952.  
  953.     /* Copy it. */
  954.     sprintf(temp,"%s",history);
  955.  
  956.     /* 
  957.      * Now run through breaking it up, setting pointers and return the number 
  958.      * of lines we found.
  959.      *
  960.      */
  961.  
  962.     for (count=0;count<HIST_LINES;count++){
  963.         if ((tmp=hist[count]=rindex(temp,'\n'))==NULL){
  964.             break;
  965.         }
  966.         *tmp='\0';
  967.         hist[count]++;
  968.     }
  969.     if (count<HIST_LINES){
  970.         hist[count++]=temp;
  971.     }
  972.     return(count);
  973. }
  974.  
  975.  
  976.  
  977. nth_hist(n)
  978. int n;
  979. {
  980.     /*
  981.      * Get the nth last filename from the list. Make use (of course) of
  982.      * read_hist and split_hist.
  983.      *
  984.      */
  985.  
  986.     register count;
  987.     register i;
  988.  
  989.     read_hist();
  990.     count=split_hist();
  991.     if (n>count-1){
  992.         if (count>1){
  993.             e_error("%s %d %s","Only",count,"history items exist.");
  994.         }
  995.         else{
  996.             e_error("%s","Only one history item exists.");
  997.         }
  998.     }
  999.     sprintf(arg,"%s",hist[n]);
  1000.  
  1001.     /* Rebuild the history with the selected name at the bottom. */
  1002.  
  1003.     reconstruct(n,count);
  1004. }
  1005.  
  1006.  
  1007.  
  1008. ask_hist()
  1009. {
  1010.     /*
  1011.      * Ask the outside world which of the files in the history is wanted.
  1012.      * set the terminal to cbreak.
  1013.      *
  1014.      */
  1015.  
  1016.     register i;
  1017.     register count;
  1018.     char *last;
  1019.     register option;
  1020.     struct sgttyb blk;
  1021.  
  1022.     /* Read and split the history file. */
  1023.     read_hist();
  1024.     count=split_hist();
  1025.  
  1026.     /* Print the history. */
  1027.     for (i=0;i<count;i++){
  1028.         fprintf(stderr,"\t[%d]: %s\n",count-i-1,hist[count-i-1]);
  1029.     }
  1030.  
  1031.     /* Give them a prompt (of sorts). */
  1032.     fprintf(stderr,"select -> ");
  1033.  
  1034.     /* Set the terminal up. */
  1035.     terminal(TERM_SET);
  1036.  
  1037.     /* Get their response. */
  1038.     option=getc(stdin);
  1039.  
  1040.     /* Make the terminal 'safe' again. */
  1041.     terminal(TERM_RESET);
  1042.  
  1043.     /* 
  1044.      * Process the option and put the appropriate file name into the 
  1045.      * arg variable.
  1046.      *
  1047.      */
  1048.  
  1049.     if (option=='\n'){
  1050.         /* They want the last file of the list. */
  1051.         fprintf(stderr,"%s\n",hist[0]);
  1052.         sprintf(arg,"%s",hist[0]);
  1053.         return;
  1054.     }
  1055.     else if (option==(int)erase){
  1056.         /* They want to leave. */
  1057.         fprintf(stderr,"\n");
  1058.         exit(1);
  1059.     }
  1060.     else if (option>='0'&&option<='0'+count-1){
  1061.         /* They have requested a file by its number. */
  1062.         option=option-'0';
  1063.         fprintf(stderr,"%s\n",hist[option]);
  1064.         sprintf(arg,"%s",hist[option]);
  1065.         reconstruct(option,count);
  1066.         return;
  1067.     }
  1068.     else{
  1069.         /* 
  1070.          * Looks like they want to name a specific file. Echo the 
  1071.          * character back to the screen.
  1072.          *
  1073.          */
  1074.  
  1075.         fprintf(stderr,"%c",option);
  1076.         arg[0]=option;
  1077.         i=1;
  1078.         while ((arg[i]=getc(stdin))!='\n'){
  1079.             i++;
  1080.         }
  1081.         arg[i]='\0';
  1082.  
  1083.         /* Seeing as they typed in the name, try and help with spelling. */
  1084.         if (!spell_help()){
  1085.             find();
  1086.         }
  1087.  
  1088.         /* If it is in the history then reconstruct and return. */
  1089.         for (i=0;i<count;i++){
  1090.             if (!strcmp(hist[i],arg)){
  1091.                 reconstruct(i,count);
  1092.                 return;
  1093.             }
  1094.         }
  1095.  
  1096.         /* Otherwise reconstruct, leaving out the oldest name. */
  1097.         reconstruct(count-1,count);
  1098.     }
  1099. }
  1100.  
  1101.  
  1102.  
  1103. FILE *
  1104. get_temp()
  1105. {
  1106.     /* Get ourselves a temporary file for the reconstructed history. */
  1107.     FILE *fp,*fopen();
  1108.  
  1109.     mktemp(tmp_file);
  1110.     if ((fp=fopen(tmp_file,"w"))==NULL){
  1111.         e_error("%s %s","Could not open temporary file",tmp_file);
  1112.     }
  1113.     return(fp);
  1114. }
  1115.  
  1116.  
  1117.  
  1118. close_temp(fp)
  1119. FILE *fp;
  1120. {
  1121.     /* Move the temporary file to be the new history. */
  1122.     FILE *fclose();
  1123.  
  1124.     if (fclose(fp)==(FILE *)EOF){
  1125.         e_error("%s %s","Could not close",tmp_file);
  1126.     }
  1127.  
  1128.     if (rename(tmp_file,HIST)!=0){
  1129.         e_error("%s %s %s %s","Could not rename",tmp_file,"to",HIST);
  1130.     }
  1131. }
  1132.  
  1133.  
  1134.  
  1135. terminal(what)
  1136. int what;
  1137. {
  1138.     /*
  1139.      * Handles the terminal. Must be first called as 
  1140.      *
  1141.      * terminal(TERM_RECORD)
  1142.      *
  1143.      * which remembers the initial terminal charcteristics and sets up the
  1144.      * "erase" variable. Thereafter can be called as
  1145.      *
  1146.      * terminal(TERM_SET)  --  to turn on CBREAK and ECHO off.
  1147.      * terminal(TERM_RESET)  --  to set the terminal to its original state.
  1148.      *
  1149.      */
  1150.  
  1151.      register i;
  1152.  
  1153.  
  1154. #ifdef sysV
  1155.     static struct termio initial_blk;
  1156.     static struct termio set_blk;
  1157. #else
  1158.     static struct sgttyb initial_blk;
  1159.     static struct sgttyb set_blk;
  1160. #endif sysV
  1161.  
  1162.  
  1163.  
  1164.     switch(what){
  1165.  
  1166.         case TERM_RECORD:{
  1167.  
  1168. #ifdef sysV
  1169.             if (ioctl(0, TCGETA, &initial_blk)==-1){
  1170.                 e_error("%s","Could not ioctl stdin.");
  1171.             }
  1172.  
  1173. #ifdef STRUCT_ASST
  1174.             /* Copy the structure in one hit. */
  1175.             set_blk=initial_blk;
  1176. #else
  1177.             /* Copy the structure field by field. */
  1178.             set_blk.c_iflag=initial_blk.c_iflag;
  1179.             set_blk.c_oflag=initial_blk.c_oflag;
  1180.             set_blk.c_cflag=initial_blk.c_cflag;
  1181.             set_blk.c_line=initial_blk.c_line;
  1182.  
  1183.             for (i=0;i<NCC;i++){
  1184.                 set_blk.c_cc[i]=initial_blk.c_cc[i];
  1185.             }
  1186. #endif STRUCT_ASST
  1187.  
  1188.             /* And now set up the set_blk. */
  1189.             set_blk.c_lflag=(initial_blk.c_lflag &= ~(ICANON|ECHO|ECHONL));
  1190.             erase=set_blk.c_cc[VERASE];
  1191.             set_blk.c_cc[VMIN]=1;
  1192.             set_blk.c_cc[VTIME]=0;
  1193. #else
  1194.             if (ioctl(0, TIOCGETP, &initial_blk)==-1){
  1195.                 e_error("%s","Could not ioctl stdin.");
  1196.             }
  1197.  
  1198. #ifdef STRUCT_ASST
  1199.             /* Copy the structure in one hit. */
  1200.             set_blk=initial_blk;
  1201. #else
  1202.             /* Copy the structure field by field. */
  1203.             set_blk.sg_ispeed=initial_blk.sg_ispeed;
  1204.             set_blk.sg_ospeed = initial_blk.sg_ospeed;
  1205.             set_blk.sg_erase = initial_blk.sg_erase;
  1206.             set_blk.sg_kill = initial_blk.sg_kill;
  1207.             set_blk.sg_flags = initial_blk.sg_flags;
  1208. #endif STRUCT_ASST
  1209.  
  1210.  
  1211.             /* And now set up the set_blk. */
  1212.             erase = set_blk.sg_erase;
  1213.  
  1214.             /* Go into CBREAK mode or stay that way if we are already. */
  1215.             set_blk.sg_flags |= CBREAK;
  1216.  
  1217.             /* Turn off echo. */
  1218.             set_blk.sg_flags &= ~ECHO;
  1219.  
  1220. #endif sysV
  1221.  
  1222.             break;
  1223.         }
  1224.  
  1225.  
  1226.  
  1227.         case TERM_SET:{
  1228.  
  1229. #ifdef sysV
  1230.             if (ioctl(0, TCSETA, &set_blk)==-1){
  1231.                 e_error("%s","Could not ioctl stdin");
  1232.             }
  1233. #else
  1234.             if (ioctl(0, TIOCSETP, &set_blk)==-1){
  1235.                 e_error("%s","Could not ioctl stdin");
  1236.             };
  1237. #endif sysV
  1238.  
  1239.             break;
  1240.         }
  1241.  
  1242.  
  1243.  
  1244.  
  1245.  
  1246.         case TERM_RESET:{
  1247.  
  1248. #ifdef sysV
  1249.             if (ioctl(0, TCSETA, &initial_blk)==-1){
  1250.                 e_error("%s","Could not ioctl stdin");
  1251.             }
  1252. #else
  1253.             if (ioctl(0, TIOCSETP, &initial_blk)==-1){
  1254.                 e_error("%s","Could not ioctl stdin");
  1255.             }
  1256. #endif sysV
  1257.  
  1258.             break;
  1259.         }
  1260.  
  1261.  
  1262.         default:{
  1263.             /* Look! - no ifdefs here. */
  1264.             e_error("%s %d","terminal() called with unknown parameter",what);
  1265.         }
  1266.     }
  1267. }
  1268.  
  1269.  
  1270.  
  1271. match(argument,pattern)
  1272. char    *argument;
  1273. char    *pattern;
  1274. {
  1275.     /*
  1276.      * Boneheaded but easy pattern matcher. Just see if the 'pattern'
  1277.      * exists anywhere in the 'argument'. Boyer-Moore who?
  1278.      * In general our patterns will be so short that it wouldn't be
  1279.      * worth the effort to set up a better algorithm.
  1280.      *
  1281.      */
  1282.  
  1283.     register length=strlen(pattern);
  1284.  
  1285.     while (strlen(argument)>=length){
  1286.         if (!strncmp(argument++,pattern,length)){
  1287.             return(1);
  1288.         }
  1289.     }
  1290.     return(0);
  1291. }
  1292.  
  1293.  
  1294.  
  1295. find_match(pattern)
  1296. char *pattern;
  1297. {
  1298.     /*
  1299.      * Find the name in the history list that contains the 'pattern'.
  1300.      * if it exists then put it into the 'arg' variable and otherwise
  1301.      * announce that a match couldn't be found and leave.
  1302.      *
  1303.      */
  1304.  
  1305.     register count;
  1306.     register i;
  1307.  
  1308.     /* Read and split the history file. */
  1309.     read_hist();
  1310.     count=split_hist();
  1311.  
  1312.     /* 
  1313.      * Try for a match with each file in turn (note that we are working
  1314.      * from most-recently-used backwards - probably a good thing).
  1315.      *
  1316.      */
  1317.  
  1318.     for (i=0;i<count;i++){
  1319.         if (match(hist[i],pattern)){
  1320.             sprintf(arg,"%s",hist[i]);
  1321.             reconstruct(i,count);
  1322.             return;
  1323.         }
  1324.     }
  1325.  
  1326.     /* We couldn't match so get out of here. */
  1327.     e_error("%s \"%s\".","Unable to match with",pattern);
  1328. }
  1329.  
  1330.  
  1331.  
  1332. insert_command(command)
  1333. char *command;
  1334. {
  1335.     /*
  1336.      * They want the last file in the history but want to preceed it
  1337.      * this time with a command - no problems here.
  1338.      *
  1339.      */
  1340.  
  1341.     register count;
  1342.     char *place;
  1343.  
  1344.     /* read and split the history. */
  1345.     read_hist();
  1346.     count=split_hist();
  1347.     
  1348.     /* 
  1349.      * If there was already a command there (indicated by a '+') then we
  1350.      * want to get rid of it. If there is a '+' but no ' ' (somewhere) after 
  1351.      * it then the history file is in disarray and we will not try to recover.
  1352.      *
  1353.      */
  1354.  
  1355.     if (*hist[0]=='+'){
  1356.         if ((place=index(hist[0],' '))==NULL){
  1357.             e_error("%s %s",HIST,"file corrupted, + but no following space");
  1358.         }
  1359.         /* Move over white space - if there is any. */
  1360.         while (*place==' '||*place=='\t'){
  1361.             place++;
  1362.         }
  1363.     }
  1364.     else{
  1365.         /* There was no command preceeding the last file in the history. */
  1366.         place=hist[0];
  1367.     }
  1368.  
  1369.     /* Put the new command and the filename into 'arg' */
  1370.     sprintf(arg,"%s %s",command,place);
  1371.  
  1372.     /* Rebuild the history with the selected command and name at the bottom. */
  1373.     reconstruct(0,count);
  1374. }
  1375.  
  1376.  
  1377.  
  1378. reconstruct(except,count)
  1379. int except;
  1380. int count;
  1381. {
  1382.     /* 
  1383.      * Reconstruct history file excepting the 'except' last.
  1384.      * So just copy all lines but the 'except'th last and then put in 'arg'
  1385.      * which contains the new line for the history.
  1386.      *
  1387.      */
  1388.  
  1389.     register i;
  1390.     FILE *tv,*get_temp();
  1391.  
  1392.     /* Get a temporary file. */
  1393.     tv=get_temp();
  1394.  
  1395.     /* Put in the lines we still want. */
  1396.     for (i=count-1;i>=0;i--){
  1397.         if (i!=except){
  1398.             fprintf(tv,"%s\n",hist[i]);
  1399.         }
  1400.     }
  1401.  
  1402.     /* Put in the new line from 'arg'. */
  1403.     fprintf(tv,"%s\n",arg);
  1404.  
  1405.     /* Rename the temporary to be the new history file. */
  1406.     close_temp(tv);
  1407. }
  1408.  
  1409.  
  1410.  
  1411. normal(string)
  1412. char *string;
  1413. {
  1414.     /* 
  1415.      * A normal filename was found, put it into arg. First of all if there
  1416.      * is a history and the file is already in it (which means they could
  1417.      * have gotten to this file in other ways), then reconstruct the history
  1418.      * as though they had. Also offer spelling help.
  1419.      *
  1420.      */
  1421.  
  1422.     register count;
  1423.     register i;
  1424.  
  1425.     /* Put it into 'arg'. */
  1426.     sprintf(arg,"%s",string);
  1427.  
  1428.     /* If there is a history file. */
  1429.     if (got_vi()){
  1430.  
  1431.         /* Read it and split it up. */
  1432.         read_hist();
  1433.         count=split_hist();
  1434.  
  1435.         /* If it is in the history then reconstruct and return. */
  1436.         for (i=0;i<count;i++){
  1437.             if (!strcmp(hist[i],arg)){
  1438.                 reconstruct(i,count);
  1439.                 return;
  1440.             }
  1441.         }
  1442.  
  1443.         /* It's not in the history, help with spelling then reconstruct. */
  1444.         if (!spell_help()){
  1445.             find();
  1446.         }
  1447.  
  1448.         /* If it is in the history then reconstruct and return. */
  1449.         for (i=0;i<count;i++){
  1450.             if (!strcmp(hist[i],arg)){
  1451.                 reconstruct(i,count);
  1452.                 return;
  1453.             }
  1454.         }
  1455.  
  1456.         reconstruct(HIST_LINES,count);
  1457.     }
  1458.     else{
  1459.  
  1460.         /* 
  1461.          * There is no history around so help with spelling and set up a 
  1462.          * history for next time.
  1463.          *
  1464.          */
  1465.  
  1466.         if (!spell_help()){
  1467.             find();
  1468.         }
  1469.         new_vi();
  1470.     }
  1471.  
  1472. }
  1473.  
  1474.  
  1475.  
  1476. multiple(number,args,size)
  1477. int number;
  1478. char **args;
  1479. {
  1480.     /*
  1481.      * There were several names on the command line so we just strcat them
  1482.      * into the 'arg' array. Check to see that the length of all the args
  1483.      * will not be greater than "size" or else we will overflow arg.
  1484.      *
  1485.      * The total argument length must be at most size-1 characters, including
  1486.      * spaces. arg needs to have a trailing '\0' so that do_vi() wont break.
  1487.      *
  1488.      */
  1489.  
  1490.     register count;
  1491.     register i;
  1492.     register total=0;
  1493.  
  1494.     *arg='\0';
  1495.     while (--number){
  1496.         if ((total+=strlen(*(args+1)))>=size){
  1497.  
  1498.             /*
  1499.              * If you are running e and you find that this condition occurs,
  1500.              * the solution is to simply increase the value of the #define
  1501.              * line for ARG_CHARS in e.h.
  1502.              *
  1503.              */
  1504.  
  1505.             fprintf(stderr,
  1506.             "%c%c%cWarning! Argument list too long, truncated after \"%s\".\n",
  1507.                 BELL,BELL,BELL,*args);      
  1508.             sleep(2);   /* Give them some chance to see what happened. */
  1509.             break;
  1510.         }
  1511.  
  1512.         strcat(arg,*++args);
  1513.         if (number>1){
  1514.             strcat(arg," ");
  1515.  
  1516.             /* 
  1517.              * Add one to total for the space. There's no need to check for
  1518.              * overflow here as we know there is another argument since
  1519.              * number > 1 still. Thus if this overflows arg, then it is going
  1520.              * to be caught anyway in the test at the top of the while loop.
  1521.              *
  1522.              */
  1523.  
  1524.             total++;                
  1525.         }
  1526.     }
  1527.  
  1528.     /*
  1529.      * Now if there is a history file and we can find an identical line
  1530.      * then reconstruct with that line at the bottom.
  1531.      *
  1532.      */
  1533.  
  1534.     if (got_vi()){
  1535.         read_hist();
  1536.         count=split_hist();
  1537.         for (i=0;i<count;i++){
  1538.             if (!strcmp(hist[i],arg)){
  1539.                 reconstruct(i,count);
  1540.                 return;
  1541.             }
  1542.         }
  1543.  
  1544.         /* 
  1545.          * Rebuild, including everything but the counth last (i.e. make
  1546.          * a new history by omitting the oldest file in the current one and
  1547.          * putting 'arg' on the end.
  1548.          *
  1549.          */
  1550.  
  1551.         reconstruct(HIST_LINES,count);
  1552.     }
  1553.     else{
  1554.         /* There was no history file so try to give them one for next time. */
  1555.         new_vi();
  1556.     }
  1557. }
  1558.  
  1559.  
  1560.  
  1561. got_vi()
  1562. {
  1563.     /* Indicate if there is a history file that they own or otherwise. */
  1564.     struct stat buf;
  1565.  
  1566.     if (stat(HIST,&buf)==-1){
  1567.         return(0);
  1568.     }
  1569.     else{
  1570.         return(getuid()==buf.st_uid);
  1571.     }
  1572. }
  1573.  
  1574.  
  1575.  
  1576. new_vi()
  1577. {
  1578.     /* 
  1579.      * Attempt to make a new history file.
  1580.      * During which several things could go wrong.
  1581.      *
  1582.      */
  1583.  
  1584.     FILE *vh,*fopen(),*fclose();
  1585.     struct stat buf;
  1586.  
  1587.     /* If you can't read the current directory, get out. */
  1588.     if (stat(".",&buf)==-1){
  1589.         e_error("%s \".\"","Could not stat");
  1590.     }
  1591.  
  1592.     /* If you own the directory (you can't get a history in /tmp). */
  1593.     if (getuid()==buf.st_uid){
  1594.  
  1595.         /* If we can't make a history, get out. */
  1596.         if ((vh=fopen(HIST,"w"))==NULL){
  1597.             e_error("%s %s %s","Could not open",HIST,"for writing.");
  1598.         }
  1599.  
  1600.         /* Put in the 'arg' that we will be vi'ing in a second. */
  1601.         fprintf(vh,"%s\n",arg);
  1602.  
  1603.         /* Close the history. */
  1604.         if (fclose(vh)==(FILE *)EOF){
  1605.             e_error("%s %s","Could not close",HIST);
  1606.         }
  1607.  
  1608.         /* Give the history some protection - for those who want it! */
  1609.         if (chmod(HIST,E_MODE)==-1){
  1610.             e_error("%s %s","Could not chmod",HIST);
  1611.         }
  1612.     }
  1613. }
  1614.  
  1615.  
  1616.  
  1617.  
  1618. spell_help()
  1619. {
  1620.     /*
  1621.        Unashamedly stolen (and modified) from "The UNIX Programming
  1622.        Environment" - Kernighan and Pike.
  1623.  
  1624.        Read the directory and if the file they want (in 'arg') does not
  1625.        exist then see if there is one that does that has similar spelling
  1626.        to what they requested. Offer the change and handle the reply.
  1627.     */
  1628.  
  1629.     register dist;
  1630.     DIR *dp, *opendir();
  1631.     struct direct *readdir();
  1632.     struct direct *entry;
  1633.     register len;
  1634.     struct stat buf;
  1635.     int ch;
  1636.  
  1637.     /* If the file already exists just return - they don't need help. */
  1638.     if (stat(arg,&buf)==0){
  1639.         return(1);
  1640.     }
  1641.  
  1642.     /* If the current directory can't be read then return. */
  1643.     if ((dp=opendir("."))==NULL){
  1644.         return(0);
  1645.     }
  1646.     
  1647.     /* Get the length of what we are seeking to cut down on strcmping time. */
  1648.     len=strlen(arg);
  1649.  
  1650.     for (entry=readdir(dp);entry!=NULL;entry=readdir(dp)){
  1651.  
  1652.         register int dlen=entry->d_namlen;
  1653.  
  1654.         /* Try to stat the entry. */
  1655.         if (stat(entry->d_name,&buf)==-1){
  1656.             continue;
  1657.         }
  1658.  
  1659.         /* If it's not a regular file then continue. */
  1660.         if ((buf.st_mode&S_IFMT)!=S_IFREG){
  1661.             continue;
  1662.         }
  1663.  
  1664.         /* 
  1665.          * If this entry has 
  1666.          *
  1667.          *      length == sought length +/- 1 
  1668.          *
  1669.          * then it should be checked.
  1670.          *
  1671.          */
  1672.  
  1673.         if (entry->d_ino && dlen>=len-1 && dlen<=len+1){
  1674.  
  1675.             /* 
  1676.              * If the distance between this name and the one the user enetered
  1677.              * is too great then just continue.
  1678.              *
  1679.              */
  1680.  
  1681.             if (sp_dist(entry->d_name,arg)==3) continue;
  1682.  
  1683.  
  1684.             /* Otherwise offer them this one. */
  1685.             terminal(TERM_SET);
  1686.             fprintf(stderr,"correct to %s [y]? ",entry->d_name);
  1687.  
  1688.             /* Get and process the reply. */
  1689.  
  1690.             ch=getc(stdin);
  1691.             terminal(TERM_RESET);
  1692.  
  1693.             if (ch=='N'){
  1694.  
  1695.                 /* No, and they mean it. Offer no more help. */
  1696.                 fprintf(stderr,"No!\n");
  1697.                 break;
  1698.             }
  1699.  
  1700.             else if (ch=='n'){
  1701.  
  1702.                 /* No, but they'd like more help. */
  1703.                 fprintf(stderr,"no\n");
  1704.                 continue;
  1705.             }
  1706.  
  1707.             else if (ch=='q'||ch=='Q'||ch==(int)erase){
  1708.  
  1709.                 /* Quit. */
  1710.                 fprintf(stderr,"quit\n");
  1711.                 closedir(dp);
  1712.                 exit(0);
  1713.             }
  1714.  
  1715.             else{
  1716.  
  1717.                 /* Yes. */
  1718.                 fprintf(stderr,"yes\n");
  1719.                 closedir(dp);
  1720.                 strcpy(arg,entry->d_name);
  1721.                 return(1);
  1722.             }
  1723.         }
  1724.     }
  1725.  
  1726.     closedir(dp);
  1727.     return(0);
  1728. }
  1729.  
  1730.  
  1731.  
  1732. sp_dist(s,t)
  1733. char *s;
  1734. char *t;
  1735. {
  1736.     /* 
  1737.      * Stolen from the same place as spell_help() above.
  1738.  
  1739.      * Work out the distance between the strings 's' and 't' according
  1740.      * to the rough metric that
  1741.      * 
  1742.      *     Identical = 0
  1743.      *     Interchanged characters = 1
  1744.      *     Wrong character/extra character/missing character = 2
  1745.      *     Forget it = 3
  1746.      *
  1747.      */
  1748.  
  1749.     while (*s++==*t){
  1750.         if (*t++=='\0'){
  1751.             /* identical */
  1752.             return(0);
  1753.         }
  1754.     }
  1755.  
  1756.     if (*--s){
  1757.         if (*t){
  1758.             if (s[1]&&t[1]&&*s==t[1]&&*t==s[1]&&!strcmp(s+2,t+2)){
  1759.                 /* Interchanged chars. */
  1760.                 return(1);
  1761.             }
  1762.             if (!strcmp(s+1,t+1)){
  1763.                 /* Wrong char. */
  1764.                 return(2);
  1765.             }
  1766.         }
  1767.         if (!strcmp(s+1,t)){
  1768.             /* Extra char in 't'. */
  1769.             return(2);
  1770.         }
  1771.     }
  1772.     if (!strcmp(s,t+1)){
  1773.         /* Extra char in 's'. */
  1774.         return(2);
  1775.     }
  1776.  
  1777.     /* Forget it. */
  1778.     return(3);
  1779. }
  1780.  
  1781.  
  1782.  
  1783.  
  1784. find()
  1785. {
  1786.     /*
  1787.      * This takes the environment variable which is #defined as PATH and 
  1788.      * extracts the directory names from it. They may be separated by 
  1789.      * arbitrary numbers of delimiter characters (currently "\n", "\t", " " 
  1790.      * and ":"). Each directory is then checked to see if it contains the 
  1791.      * desired filename (with a call to check). Spelling corrections are 
  1792.      * not attempted.
  1793.      *
  1794.      */
  1795.  
  1796.     extern char *getwd();
  1797.     extern char *getenv();
  1798.     char *p;
  1799.     char path[MAX_PATH];
  1800.     char *dir;
  1801.     char *space;
  1802.     char *current_dir;
  1803.     char wd[MAXPATHLEN];
  1804.     char what[ARG_CHARS];
  1805.  
  1806.     if (!(p=getenv(E_PATH))) return;
  1807.  
  1808.     if (strlen(p)>=MAX_PATH){
  1809.         e_error("%s %s %s %d.","Length of",E_PATH,"variable exceeds",MAX_PATH);
  1810.     }
  1811.  
  1812.     strcpy(path,p);
  1813.     strcpy(what,arg);
  1814.  
  1815.     if (!(current_dir=getwd(wd))){
  1816.         e_error("%s","Could not get working directory.");
  1817.     }
  1818.  
  1819.     dir=path;
  1820.  
  1821.     /* Skip initial delimiters in the PATH variable. */
  1822.     while (*dir && is_delim(dir)) dir++;
  1823.  
  1824.     if (!*dir) return(0);   /* There was nothing there but delimiters! */
  1825.  
  1826.     space=dir+1;
  1827.  
  1828.     while (*space){
  1829.  
  1830.         /* Move "space" along to the first non delimiter. */
  1831.         while (*space && !is_delim(space)) space++;
  1832.  
  1833.         if (*space){
  1834.             *space='\0';
  1835.             space++;
  1836.         }
  1837.  
  1838.         /* Skip any white space between directory names. */
  1839.         while (*space && is_delim(space)) space++;
  1840.  
  1841.         /* Check the directory "dir" for the filename "what". */
  1842.         if (check(what,dir)){
  1843.  
  1844.             /* Offer them dir/what. */
  1845.             terminal(TERM_SET);
  1846.             fprintf(stderr,"%s/%s [y]? ",dir,what);
  1847.  
  1848.             /* Process the reply. */
  1849.             switch (getc(stdin)){
  1850.                 case 'N':
  1851.                 case 'n':{
  1852.                     fprintf(stderr,"no\n");
  1853.                     terminal(TERM_RESET);
  1854.                     break;
  1855.                 }
  1856.  
  1857.                 case 'q':
  1858.                 case 'Q':{
  1859.                     fprintf(stderr,"quit\n");
  1860.                     terminal(TERM_RESET);
  1861.                     exit(0);
  1862.                     break;
  1863.                 }
  1864.  
  1865.                 default :{
  1866.                     fprintf(stderr,"yes\n");
  1867.                     terminal(TERM_RESET);
  1868.                     if (chdir(current_dir)==-1){
  1869.                         e_error("%s %s","Could not chdir to",current_dir);
  1870.                     }
  1871.                     sprintf(arg,"%s/%s",dir,what);
  1872.                     return(1);
  1873.                 }
  1874.             }
  1875.         }
  1876.         dir=space;
  1877.     }
  1878.  
  1879.     /* Go back to the original directory. */
  1880.     if (chdir(current_dir)==-1){
  1881.         e_error("%s %s","Could not chdir to",current_dir);
  1882.     }
  1883.     return(0);
  1884. }
  1885.  
  1886.  
  1887.  
  1888. check(target,dir)
  1889. char *target;
  1890. char *dir;
  1891. {
  1892.     /*
  1893.      * Checks to see if the name "target" can be found in the directory "dir".
  1894.      * 
  1895.      */
  1896.  
  1897.     DIR *dp, *opendir();
  1898.     struct direct *readdir();
  1899.     struct direct *entry;
  1900.     struct stat buf;
  1901.  
  1902.     if ((dp=opendir(dir))==NULL){
  1903.         fprintf(stderr,"Cannot open \"%s\"\n",dir);
  1904.         return(0);
  1905.     }
  1906.     
  1907.     for (entry=readdir(dp);entry!=NULL;entry=readdir(dp)){
  1908.         if (!strcmp(entry->d_name,target)){
  1909.             if (chdir(dir)==-1){
  1910.                 perror("chdir");
  1911.                 return(0);
  1912.             }
  1913.  
  1914.             if (stat(entry->d_name,&buf)==-1){
  1915.                 /*
  1916.                  * At this point I used to have perror() give a message and
  1917.                  * the function return. Then one day e ran across an unresolved
  1918.                  * symbolic link at this point. The filename existed in the
  1919.                  * search directory, but it could not be stat'd as the thing
  1920.                  * it was supposedly linked to had been removed.
  1921.                  *
  1922.                  * The easiest thing (I think) to do is to ignore it. 
  1923.                  */
  1924.  
  1925.                 fprintf(stderr,
  1926.                     "%c%c%cWarning: Suspected unresolved symbolic link %s/%s\n",
  1927.                     BELL,BELL,BELL,dir,entry->d_name);
  1928.                 sleep(2);
  1929.                 continue;
  1930.             }
  1931.  
  1932.             /* 
  1933.                 If it is not a directory and EITHER you own it and can
  1934.                 read it OR you don't own it and it is readable by others, 
  1935.                 OR you are in the group of the owner and it's group readable
  1936.                     - then this is it.
  1937.             */
  1938.  
  1939.             if (    ((buf.st_mode&S_IFMT)==S_IFREG)  &&  
  1940.                     (
  1941.                         (buf.st_uid==getuid() && buf.st_mode&S_IREAD)
  1942.                         ||
  1943.                         (buf.st_gid==getgid() && buf.st_mode&G_READ)
  1944.                         ||
  1945.                         (buf.st_uid!=getuid() && buf.st_mode&O_READ)
  1946.                     )
  1947.                 )
  1948.             {
  1949.                 return(1);
  1950.             }
  1951.         }
  1952.     }
  1953.     return(0);
  1954. }
  1955.  
  1956.  
  1957.  
  1958. /* VARARGS1 */
  1959. e_error(a,b,c,d,e,f)
  1960. char *a;
  1961. {
  1962.     /*
  1963.      * Print the error message, clean up and get out.
  1964.      *
  1965.      */
  1966.  
  1967.     fprintf(stderr,"%s: ",myname);
  1968.     fprintf(stderr,a,b,c,d,e,f);
  1969.     fputc('\n',stderr);
  1970.     terminal(TERM_RESET);
  1971.     unlink(tmp_file);
  1972.     exit(1);
  1973. }
  1974. @//E*O*F e.c//
  1975. chmod u=rw,g=rx,o=rx e.c
  1976.  
  1977. echo Inspecting for damage in transit...
  1978. temp=/tmp/shar$$; dtemp=/tmp/.shar$$
  1979. trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
  1980. cat > $temp <<\!!!
  1981.       56     254    1791 README
  1982.      192     890    6788 e.cat
  1983.      191    1008    5781 e.1
  1984.       14      37     208 Makefile
  1985.       10      18     111 MANIFEST
  1986.       61     303    2271 MODS
  1987.      151     375    2697 e.h
  1988.     1429    3783   35152 e.c
  1989.     2104    6668   54799 total
  1990. !!!
  1991. wc  README e.cat e.1 Makefile MANIFEST MODS e.h e.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
  1992. if [ -s $dtemp ]
  1993. then echo "Ouch [diff of wc output]:" ; cat $dtemp
  1994. else echo "No problems found."
  1995. fi
  1996. exit 0
  1997.  
  1998.  
  1999.